﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DataSimDLL;
using System.Diagnostics;
using System.IO;

namespace BDSim
{
    /// <summary>
    /// 单个接收机对所有可见星的观测数据生成
    /// </summary>
    public class ObsDataGenModel
    {
        //输入参数
        public UTCTime CurTime;//观测数据生成时刻
        public int SatNum;//系统中卫星总数
        public List<int> SatsID;//卫星的编号，与卫星的位置速度信息列表中的顺序对应
        public List<string> SatNameList;//卫星唯一标记，作为输出时的标记
        public List<List<NodePosVel>> SatsPV;//所有卫星的一段时间内的位置速度数据（含10个点）
        public List<double> SatsClk;//所有卫星的钟差
        public int SatOrbFlag;//0:ECI_pos 1:ECI_(pos+vel)
        public double Elelim;//观测截止仰角

        public int RecID;//接收机编号
        public NodePosVel RecPV;//接收机位置

        public int FreNum;//接收机的频点数
        public List<EFrePoint> Fres;//接收机使用的频率

        //public ObsErrFlag ErrFlag;//各项误差开关 
        public MeteorologicalsParam MetePara;//气象参数
        public PosENUClass PosENU;//接收机相位中心偏移误差（ENU方向）
        public double[] SatPhaseOff;//卫星相位中心偏移
        public double PseudoRanThH;//伪距噪声量级
        public double CarrRanThH;//载波相位噪声量级

        public int IsSatClk;// 卫星钟差开关，0：不考虑，1：多项式模型
        public int IsStaClk;// 接收机钟差开关，0：不考虑，1：
        public int IsIion;// 电离层开关，0：不考虑，1：15阶球谐模型，2：全球格网模型
        public int IsItro;// 对流层开关，0：不考虑，1：Hopfield,2：ImprovedHopfield,3：Saastamoinen,
        public int IsIrel;// 相对论效应开关，0：不考虑，1：
        //随机误差赋值
        public CalRan_Input pCalRan_Input;
        //private Stopwatch stopWatch = new Stopwatch();
        #region 中间参数
        ObsReceiverInPut input;
        double ErrValue = 0;
        ObsReceiverOutput output;
        ObsErrOutput RecObsErrOutput;//单个时刻单颗可见星的根据卫星名称保存观测数据

        #endregion 中间参数


        public List<List<ObsReceiverOutput>> RecObsOutput;//单个时刻所有可见星的输出数据
        public Dictionary<string, List<ObsReceiverOutput>> RecObsOutputDic;//单个时刻所有可见星的根据卫星名称保存观测数据

        public List<ObsErrOutput> RecObsErrOutputList;//单个时刻所有可见卫星的观测误差

        /// <summary>
        /// 几何距离计算参数初始化(实时计算和初始化使用的函数相同)
        /// </summary>
        public void ObsDataGen()
        {
            input.StaID = RecID;
            //input.ReceiverPos = RecPV;
            //  input.DeltRanThH = new double[2];
            input.SendPos = new NodePosVel[10];
            input.ObsErr = new double[2];

            RecObsOutput = new List<List<ObsReceiverOutput>>();
            RecObsOutputDic = new Dictionary<string, List<ObsReceiverOutput>>();
            RecObsErrOutputList = new List<ObsErrOutput>();
            NodePosVel tempecf = new NodePosVel();

            for (int i = 0; i < SatNum; i++)
            {
                input.CurTime = CurTime;
                input.SatID = SatsID[i];
                input.SendPosFlag = SatOrbFlag;

                //根据10个点的卫星轨道值计算CurTime时刻的卫星位置、速度信息
                int num = 10;//拉格朗日插值
                double[] satT = new double[num];
                double[] satX = new double[num];
                double[] satY = new double[num];
                double[] satZ = new double[num];
                double[] satVX = new double[num];
                double[] satVY = new double[num];
                double[] satVZ = new double[num];

                for (int j = 0; j < 10; j++)//卫星位置赋值
                {
                    input.SendPos[j] = SatsPV[i][j];

                    //根据10个点的卫星轨道值计算CurTime时刻的卫星位置、速度信息
                    satT[j] = SatsPV[i][j].Epoch.JD;
                    satX[j] = SatsPV[i][j].X;
                    satY[j] = SatsPV[i][j].Y;
                    satZ[j] = SatsPV[i][j].Z;
                    satVX[j] = SatsPV[i][j].VX;
                    satVY[j] = SatsPV[i][j].VY;
                    satVZ[j] = SatsPV[i][j].VZ;
                    //根据10个点的卫星轨道值计算CurTime时刻的卫星位置、速度信息
                }

                double CalEle = 0;
                double CalAzi = 0;

                //根据10个点的卫星轨道值计算CurTime时刻的卫星位置、速度信息
                NodePosVel temppv = new NodePosVel();//CurTime时刻的卫星ECI位置、速度信息
                temppv.Epoch = CurTime;
                CInterpolateDLL.Lagrange(CurTime.JD, num, satT, satX, ref temppv.x);
                CInterpolateDLL.Lagrange(CurTime.JD, num, satT, satY, ref temppv.y);
                CInterpolateDLL.Lagrange(CurTime.JD, num, satT, satZ, ref temppv.z);
                CInterpolateDLL.Lagrange(CurTime.JD, num, satT, satVX, ref temppv.vx);
                CInterpolateDLL.Lagrange(CurTime.JD, num, satT, satVY, ref temppv.vy);
                CInterpolateDLL.Lagrange(CurTime.JD, num, satT, satVZ, ref temppv.vz);
                //根据10个点的卫星轨道值计算CurTime时刻的卫星位置、速度信息

                TimeSpaceDll.ECI2ECF(temppv, 0, 0, out tempecf);


                if (1 == CAccessCalDLL.StationAccess(RecPV, tempecf, ECFtype.type_CGCS2000, Elelim, out CalEle, out CalAzi))
                {
                    RecObsErrOutput.SatID = SatsID[i];//观测误差卫星号赋值
                    //观测误差计算

                    //stopWatch.Start();
                    #region 误差计算
                    if (1==IsSatClk)//卫星钟差
                    {
                        ErrValue = -SatsClk[i] * 2.99792458E8;
                    }
                    else
                        ErrValue = 0;
                    //if ( ErrFlag.IsStaClk)
                    //{

                    //}
                    //else
                    //    ErrValue += 0;

                    if (IsItro!=0)//对流层误差
                    {
                        CalTro_Input troinput = new CalTro_Input();
                        troinput.SatECFPos = new double[3];
                        troinput.UsePos = new double[3];

                        troinput.Tro = MetePara;
                        troinput.SatECFPos[0] = tempecf.x;
                        troinput.SatECFPos[1] = tempecf.y;
                        troinput.SatECFPos[2] = tempecf.z;
                        troinput.UsePos[0] = RecPV.x;
                        troinput.UsePos[1] = RecPV.y;
                        troinput.UsePos[2] = RecPV.z;

                        double DeltTro = 0;
                        TropDLL.CalTro_output(troinput, IsItro, out DeltTro);//采用Saastamoninen模型＋Chao和CFA映射函数
                        ErrValue += DeltTro;

                        RecObsErrOutput.DeltTro = DeltTro;//观测误差对流层误差赋值
                    }
                    else
                        ErrValue += 0;

                    //stopWatch.Stop();
                    //Console.WriteLine("对流层误差仿真：Parallel run " + stopWatch.ElapsedMilliseconds + " ms.\n");
                    //stopWatch.Reset();
                    //stopWatch.Start();

                    //if ( ErrFlag.IsImul)//多路径误差
                    //{

                    //}
                    //else
                    //    ErrValue += 0;
                    //if ( ErrFlag.IsIoff)//相位中心偏移误差
                    //{
                    //    double DeltOff = 0;
                    //    caloff_input pCalOff_Input;

                    //    pCalOff_Input.CurTime = CurTime;

                    //    pCalOff_Input.SatECFPos = new double[3];
                    //    pCalOff_Input.SatECFPos[0] = tempecf.x;
                    //    pCalOff_Input.SatECFPos[1] = tempecf.y;
                    //    pCalOff_Input.SatECFPos[2] = tempecf.z;

                    //    pCalOff_Input.UseECFPos = new double[3];
                    //    pCalOff_Input.UseECFPos[0] = RecPV.x;
                    //    pCalOff_Input.UseECFPos[1] = RecPV.y;
                    //    pCalOff_Input.UseECFPos[2] = RecPV.z;

                    //    pCalOff_Input.S_Off = new double[3];
                    //    pCalOff_Input.S_Off = SatPhaseOff;

                    //    pCalOff_Input.U_Off = new double[3];
                    //    pCalOff_Input.U_Off[0] = PosENU.E;
                    //    pCalOff_Input.U_Off[1] = PosENU.N;
                    //    pCalOff_Input.U_Off[2] = PosENU.U;

                    //    COffSetDLL.CalPhaseOffset_output(pCalOff_Input, 1, out DeltOff);
                    //    RecObsErrOutput.DeltOff = DeltOff;

                    //    ErrValue += DeltOff;
                    //}
                    //else
                    //    ErrValue += 0;

                    //stopWatch.Stop();
                    //Console.WriteLine("相位中心偏移误差仿真：Parallel run " + stopWatch.ElapsedMilliseconds + " ms.\n");
                    //stopWatch.Reset();
                    //stopWatch.Start();

                    if (IsIrel!=0)
                    {
                        double DeltRel = 0.0;
                        RelDLL.CalRel_output(temppv, 1, out DeltRel);
                        ErrValue += DeltRel;
                    }
                    else
                        ErrValue += 0;


                    //stopWatch.Stop();
                    //Console.WriteLine("相对论效应仿真：Parallel run " + stopWatch.ElapsedMilliseconds + " ms.\n");


                    #endregion 误差计算

                    List<ObsReceiverOutput> RecSingleFreObsOutput = new List<ObsReceiverOutput>();
                    RecObsErrOutput.DeltIon = new double[3] { 0, 0, 0 };
                    for (int k = 0; k < FreNum; k++)
                    {
                        FreFlagValue freflag = new FreFlagValue();
                        input.Freq = freflag.FreDic[Fres[k]];
                        switch (Fres[k])
                        {
                            case EFrePoint.GLONASS_L1:
                                input.Freq += freflag.DicGloFrq[SatsID[i]] * 0.5625;//9/16
                                break;
                            case EFrePoint.GLONASS_L2:
                                input.Freq += freflag.DicGloFrq[SatsID[i]] * 0.4375;//7 / 16
                                break;
                            default:
                                break;
                        }

                        if (IsIion!=0)//电离层误差计算
                        {
                            CalIon_Input ioninput;
                            ioninput.Time = CurTime;
                            ioninput.SatECFPos = new double[3];
                            ioninput.SatECFPos[0] = tempecf.x;
                            ioninput.SatECFPos[1] = tempecf.y;
                            ioninput.SatECFPos[2] = tempecf.z;
                            ioninput.UsePos = new double[3];
                            ioninput.UsePos[0] = RecPV.x;
                            ioninput.UsePos[1] = RecPV.y;
                            ioninput.UsePos[2] = RecPV.z;

                            CalIon_Output ionoutput;
                            IonTEC.CalIon_output(ioninput, IsIion, out ionoutput);

                            double DeltIon = 0;
                            IonMapDLL.Ion_VTEC2DST(ionoutput.VTEC, input.Freq, ionoutput.Zenith, 1, out DeltIon);

                            input.ObsErr[0] = ErrValue + DeltIon;
                            input.ObsErr[1] = ErrValue - DeltIon;

                            RecObsErrOutput.DeltIon[k] = DeltIon;//观测误差电离层赋值
                        }
                        else
                        {
                            input.ObsErr[0] = ErrValue + 0;
                            input.ObsErr[1] = ErrValue - 0;
                        }

                        //频间差
                        //if (k==0)
                        //{
                        //    input.ObsErr[0] += 2.29E-9 * 2.99792458E8;
                        //    input.ObsErr[1] += 2.29E-9 * 2.99792458E8;
                        //}
                        //else if (k == 1)
                        //{
                        //    input.ObsErr[0] += 1.12E-9 * 2.99792458E8;
                        //    input.ObsErr[1] += 1.12E-9 * 2.99792458E8;
                        //}

                        //input.DeltRanThH[0] = 0;
                        //input.DeltRanThH[1] = 0;

                        double CarriLength = 2.99792458E8 / (input.Freq * 1.0E6);
                        NodePosVel receci = new NodePosVel();
                        RecPV.epoch = CurTime;
                        TimeSpaceDll.ECF2ECI(RecPV, 0, 0, out receci);
                        input.ReceiverPos = receci;

                        CObsDataGenDLL.ObsRecvInit(input, out output);

                        pCalRan_Input.DeltRanThH = PseudoRanThH;
                        double PseudoRan = NoiseServiceDLL.CalRan_output(ref pCalRan_Input, 1);

                        pCalRan_Input.DeltRanThH = CarrRanThH;
                        double CarrRan = NoiseServiceDLL.CalRan_output(ref pCalRan_Input, 1);

                        output.SatObs.A_Pseudo_writein += PseudoRan;
                        output.SatObs.A_Carri_writein += CarrRan / CarriLength; ;
                        RecSingleFreObsOutput.Add(output);
                    }
                    RecObsOutput.Add(RecSingleFreObsOutput);
                    RecObsOutputDic.Add(SatNameList[i], new List<ObsReceiverOutput>());
                    RecObsOutputDic[SatNameList[i]] = RecSingleFreObsOutput;
                    RecObsErrOutputList.Add(RecObsErrOutput);//观测误差保存进列表

                }

            }
        }

    }
}
